//url:https://github.com/YongzeYao/the-super-tiny-compiler-CN


//例：(add 2 (subtract 4 2))==>[{ type: 'paren', value: '(' }, ...]
// 1.词法分析(词素生成器)
function tokenizer(input){
    let current=0,tokens=[]
    while(current<input.length){
        let char=input[current]
        if(char==='('){
            tokens.push({
                type:'paren',
                value:'('
            })
            current++
            continue
        }
        if(char===')'){
            tokens.push({
                type:'paren',
                value:')'
            })
            current++
            continue
        }
        const WHILESPACE=/\s/
        if(WHILESPACE.test(char)){
            current++
            continue
        }
        let NUMBERS=/[0-9]/
        if(NUMBERS.test(char)){
            let value=''
            while(NUMBERS.test(char)){
                value+=char
                char=input[++current]
            }
            tokens.push({
                type:'number',
                value
            })
            continue
        }
        if(char==='"'){
            let str=""
            //跳过字符串开始时的那个引号
            char=input[++current]
            while(char!=='"'){
                str+=char
                char=input[++current]
            }
            //跳过字符串结束时的那个引号
            char=input[++current]
            tokens.push({
                type:'string',
                value:str
            })
            continue
        }
        //和字符串的区别，字符串开始的第一个必定是"
        let LEFTERS=/[a-z]/i  
        if(LEFTERS.test(char)){
            let value=''
            while(LEFTERS.test(char)){
                value+=char
                char=input[++current]
            }
            tokens.push({
                type:'name',
                value
            })
            continue
        }

        throw new TypeError('I dont know what this character is: ' + char)
    }
    return tokens
}

//语法分析(生成抽象语法树ast)  [{ type: 'paren', value: '(' }, ...]   =>   { type: 'Program', body: [...] }
function parser(tokens){
    let current=0
    function walk(){
        let token=tokens[current]
        if(token.type==="number"){
            current++
            return {
                type:'NumberLiteral',
                value:token.value
            }
        }
        if(token.type==="string"){
            current++
            return {
                type:'StringLiteral',
                value:token.value
            }
        }
        if(token.type==="paren"&&token.value==="("){
            token=tokens[++current]
            let node={
                type:'CallExpression',
                name:token.value,
                params:[]
            }
            token=tokens[++current]
            while(token.type!=="paren"||(token.type==='paren'&&token.value!==')')){
                node.params.push(walk())
                token=tokens[current]
            }
            //跳过闭括号
            current++
            return node
        }
        throw new TypeError(token.type)
    }
    let ast={
        type:'Program',
        body:[]
    }
    while(current<tokens.length){
        ast.body.push(walk())
    }
    return ast
}

//traverser函数(访问者对象) 碰到一个节点的时候调用访问者对象相应的方法
function traverser(ast,visitor){
    function traverseArray(array,parent){
        array.forEach(child=>{
            traverseNode(child,parent)
        })
    }
    function traverseNode(node,parent){
        let methods=visitor[node.type]
        if(methods&&methods.enter){
            methods.enter(node,parent)
        }
        switch(node.type){
            case "Program":
                traverseArray(node.body,node)
                break;
            case "CallExpression":
                traverseArray(node.params,node)
                break;
            case 'NumberLiteral':
            case 'StringLiteral':
                break;
            default:
                throw new TypeError(node.type)    
        }
        if (methods && methods.exit) {
            methods.exit(node, parent);
          }
    }
    traverseNode(ast, null);
}

//transformer AST
function transformer(ast){
    let newAst={
        type:'Program',
        body:[]
    }
    ast._context=newAst.body
    traverser(ast,{
        // 访问者对象处理的第一种情况是`NumberLiteral`节点。
        NumberLiteral:{
            enter(node,parent){
                parent._context.push({
                    type:'NumberLiteral',
                    value:node.value
                })
            }
        },
        // 接下来是`StringLiteral`类型节点。
        StringLiteral:{
            enter(node,parent){
                parent._context.push({
                    type:'StringLiteral',
                    value:node.value
                })
            }
        },
        // 接着是`CallExpression`类型节点。
        CallExpression:{
            enter(node,parent){
                let expression={
                    type:"CallExpression",
                    callee:{
                        type:'Identifier',
                        name: node.name,
                    },
                    arguments:[]
                };
                node._context = expression.arguments;
                if(parent.type!=="CallExpression"){
                    expression={
                        type:'ExpressionStatement',
                        expression: expression,
                    }
                }
                parent._context.push(expression);
            }
        }
    })
    return newAst
}


//代码生成（code gen）
function codeGenerator(node){
    switch(node.type){
        case 'Program':
            return node.body.map(codeGenerator).join('\n')
        case 'ExpressionStatement':
            return (
                codeGenerator(node.expression)
            )
        case 'CallExpression':
            return (
                codeGenerator(node.callee)+"("+node.arguments.map(codeGenerator).join(', ')+')'
            )
         case 'Identifier':
            return node.name;
         case 'NumberLiteral':
             return node.value;
         case 'StringLiteral':
             return '"' + node.value + '"';
         default:
             throw new TypeError(node.type)
    }
}
// var o={
//     type: 'Program',
//     body: [{
//       type: 'ExpressionStatement',
//        expression: {
//         type: 'CallExpression',
//         callee: {
//            type: 'Identifier',
//           name: 'add'
//         },
//          arguments: [{
//            type: 'NumberLiteral',
//           value: '2'
//         }, {
//           type: 'CallExpression',
//            callee: {
//              type: 'Identifier',
//              name: 'subtract'
//           },
//            arguments: [{
//             type: 'NumberLiteral',
//              value: '4'
//            }, {
//              type: 'NumberLiteral',
//              value: '2'
//           }]
//         }]
//       }
//      }]
//   }

console.log(codeGenerator(transformer(parser(tokenizer("(add 2 (subtract 4 2))")))))
